import { any } from "../../../core/logic";
import { Engine } from "../../../Engine";
import { Pass } from "../Pass";
import { Effect, EffectType } from "./Effect";
import { fxaa_frag } from "./shaders/fxaa_frag";
import { fxaa_vert } from "./shaders/fxaa_vert";
/**
 * 后处理快色抗锯齿。
 * @class
 * @memberof w.effects
 */
class Fxaa extends Effect {
    /**
     * @constructor
     * @param {*} opts 
     * @param {Number} opts.FXAA_REDUCE_MIN 缺省值1.0/128.0
     * 这个参数调整了锯齿减少量的乘数。较大的值会使FXAA减少更多的锯齿，但可能会导致一些细节的模糊。
     * 较小的值则会减少细节的模糊，但也可能对锯齿的减少效果减弱。
     * 你可以根据需要调整这个值以达到最佳效果。
     * @param {Number} opts.FXAA_REDUCE_MUL 缺省值1.0/8.0
     * 这个参数控制着带宽的最大跨度。较大的值可以处理更大的边缘锯齿，但也可能导致一些细节的模糊。
     * 较小的值则可以保留更多细节，但可能无法充分减少边缘锯齿。
     * 你可以根据需要进行调整，以平衡锯齿减少和细节保留之间的效果。
     * @param {Number} opts.FXAA_SPAN_MAX 缺省值8.0
     * 这个参数控制着对锯齿的最小减少量。较低的值会使FXAA更积极地减少锯齿，但也可能导致一些细节的损失。
     * 较高的值则会减少细节损失，但可能对锯齿的减少效果不够明显。
     * 你可以尝试不同的值来找到最佳的平衡点。
     */
    constructor(opts = {}) {
        super(opts);
        this.type=EffectType.Fxaa;
        this.FXAA_REDUCE_MIN = any(opts.FXAA_REDUCE_MIN, 1.0 / 128.0);
        this.FXAA_REDUCE_MUL = any(opts.FXAA_REDUCE_MUL, 1.0 / 8.0);
        this.FXAA_SPAN_MAX = any(opts.FXAA_SPAN_MAX, 8.0);
        this.sampler = Engine.instance.device.createSampler({
            magFilter: 'linear',//nearest linear
            minFilter: 'linear'
        });
        this.initBuffer();
        this.initPipeLine();
    }
    initBuffer() {
        this.resolutionBuffer = Engine.instance.device.createBuffer({
            label: "resolution",
            size: 8,
            usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
        });
    }
    initPipeLine() {
        this.bindGroupLayout = Engine.instance.device.createBindGroupLayout({
            label: "layout",
            entries: [
                {
                    binding: 0,
                    visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
                    buffer: {
                        type: "uniform"
                    },
                },
                {
                    binding: 1,
                    visibility: GPUShaderStage.FRAGMENT,
                    sampler: {
                        type: "filtering"
                    },
                },
                {
                    binding: 2,
                    visibility: GPUShaderStage.FRAGMENT,
                    texture: {
                        sampleType: "float",
                        viewDimension: "2d",
                        multisampled: false
                    }
                }]
        });
        let pipeLayout = Engine.instance.device.createPipelineLayout({ bindGroupLayouts: [this.bindGroupLayout] });
        this.pipeLine = Engine.instance.device.createRenderPipeline({
            label: 'Bloom Pipline',
            layout: pipeLayout,
            vertex: {
                module: Engine.instance.device.createShaderModule({
                    code: fxaa_vert,
                }),
                entryPoint: 'main'
            },
            fragment: {
                module: Engine.instance.device.createShaderModule({
                    code: fxaa_frag,
                }),
                constants: {
                    FXAA_REDUCE_MIN: this.FXAA_REDUCE_MIN,
                    FXAA_REDUCE_MUL: this.FXAA_REDUCE_MUL,
                    FXAA_SPAN_MAX: this.FXAA_SPAN_MAX,
                },
                entryPoint: 'main',
                targets: [
                    {
                        format: Engine.instance.format
                    }
                ]
            },
            primitive: {
                topology: 'triangle-list',
                cullMode: 'back',
                frontFace: 'ccw'
            }
        });
    }
    initBindGroup(texture) {
        if(this.group && this.group.id!=texture.id){
            this.group=null;
        }
        if(!this.group){
            this.group=Engine.instance.device.createBindGroup({
                label: 'Texture Group with Texture/Sampler',
                layout: this.bindGroupLayout,
                entries: [
                    {
                        binding: 0,
                        resource: {
                            buffer: this.resolutionBuffer,
                            size: 8,
                        }
                    },
                    {
                        binding: 1,
                        resource: this.sampler
                    },
                    {
                        binding: 2,
                        resource: texture.view
                    }
                ]
            });
            this.group.id=texture.id;
        }
        return this.group;

    }
    onRender(commandEncoder) {
        //执行帧渲染
        const pass = super.onRender(commandEncoder);
        const texture = Pass.instance().readAttach;
        const renderPass = commandEncoder.beginRenderPass(pass);
        renderPass.setPipeline(this.pipeLine);
        let bindGroup = this.initBindGroup(texture);
        // set uniformGroup
        Engine.instance.queue.writeBuffer(
            this.resolutionBuffer,
            0,
            new Float32Array(Engine.instance.size)
        );

        renderPass.setBindGroup(0, bindGroup)
        renderPass.draw(3)
        renderPass.end()
    }
    getDescript() {
        if (this._descript) {
            if (this._descript.size[0] !== Engine.instance.size[0] || this._descript.size[1] !== Engine.instance.size[1]) {
                this._descript = null;
            }else{
                this._descript.colorAttachments[0].view=Pass.instance().writeAttach.view;
            }
        }
        if (!this._descript) {
            this._descript = {
                colorAttachments: [
                    {
                        view: Pass.instance().writeAttach.view,
                        loadOp: 'clear',
                        clearValue: { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
                        storeOp: 'store',
                    }
                ]
            }
            this._descript.size = Engine.instance.size;
        }
        return this._descript;
    }
}
export { Fxaa }